package org.jpropelleralt.jview.list.impl;

import java.awt.event.ActionEvent;
import java.awt.event.KeyEvent;
import java.util.Map;

import javax.swing.AbstractAction;
import javax.swing.Icon;

import org.jpropelleralt.box.Box;
import org.jpropelleralt.box.Boxes;
import org.jpropelleralt.change.Change;
import org.jpropelleralt.jview.JViews;
import org.jpropelleralt.jview.icon.IconFactory.IconSize;
import org.jpropelleralt.jview.messages.Messages;
import org.jpropelleralt.list.ListBox;
import org.jpropelleralt.plumbing.Target;
import org.jpropelleralt.ref.Ref;
import org.jpropelleralt.view.Views;
import org.jpropelleralt.view.update.UpdatableView;
import org.jpropelleralt.view.update.UpdateManager;

/**
 * An action allowing for an element to be deleted from the {@link ListBox}
 * contained in a {@link Ref}.
 * The index contained in an {@link Integer} {@link Ref} is used as the 
 * deletion index. Deleted elements are sent to a {@link Target}
 *  
 * @param <T>		The type of element in the list 
 */
public class ListDeleteAction<T> extends AbstractAction implements UpdatableView {

	private final static Icon icon = JViews.getIconFactory().getIcon(IconSize.SMALL, "actions", "list-remove");
	
	private final Ref<ListBox<T>> list;
	private final Ref<Integer> selectionIndex;
	private final Target<T> target;
	private final Ref<Boolean> locked;

	private final UpdateManager updateManager;
	

	private ListDeleteAction(
			Ref<ListBox<T>> list,
			Ref<Integer> selectionIndex,
			Target<T> target,
			Ref<Boolean> locked,
			String text, Icon icon,
            String desc, Integer mnemonic) {
		
		super(text, icon);
		
		putValue(SHORT_DESCRIPTION, desc);
		
		if (mnemonic != null && mnemonic >= 0) {
			putValue(MNEMONIC_KEY, mnemonic);
		}
		
		this.list = list;
		this.selectionIndex = selectionIndex;
		this.target = target;
		this.locked = locked;
		
		updateManager = Views.updateManager();
		updateManager.registerUpdatable(this);

		//Listen to the list and selection, we can use this to
		//enable and disable the action
		list.features().addView(this);
		selectionIndex.features().addView(this);

		if (locked != null) {
			locked.features().addView(this);
		}
		
		//Start out updated
		updateManager.updateRequiredBy(this);
	}

	@Override
	public void changes(Map<Box, Change> changes) {
		updateManager.updateRequiredBy(this);
	}
	
	/**
	 * Create a {@link ListDeleteAction}
	 * @param list				{@link Ref} to the {@link ListBox} to act on
	 * @param selectionIndex	{@link Ref} to the selection index of
	 * 							the item to delete.
	 * @param target			The target for deleted elements
	 * @param locked			{@link Ref} that controls editing - when true, 
	 * 							button is disabled, otherwise enabled. 
	 * 							If null, editing is always enabled.  
	 * @param text				Description text for action
	 * @param icon				Icon for action
	 * @param desc				Short description for action (e.g. for tooltip)
	 * @param mnemonic			Mnemonic for action, should be a {@link KeyEvent} 
	 * 							value, or negative to have no mnemonic
	 * 							e.g. {@link KeyEvent#VK_A}
	 * @return					A new {@link ListDeleteAction}
	 * @param <T>				The type of element in the list 
	 */
	public static <T> ListDeleteAction<T> create(Ref<ListBox<T>> list,
			Ref<Integer> selectionIndex,
			Target<T> target,
			Ref<Boolean> locked,
			String text, Icon icon,
            String desc, Integer mnemonic) {
		return new ListDeleteAction<T>(list, selectionIndex, target, locked, text, icon, desc, mnemonic);
	}
	
	/**
	 * Create an action with default icon. Text, mnemonic and tooltip set by resources.
	 * @param list				{@link Ref} to the {@link ListBox} to act on
	 * @param selectionIndex	{@link Ref} to the selection index of
	 * 							the item to delete.
	 * @param target			The target for deleted elements
	 * @param locked			{@link Ref} that controls editing - when true, 
	 * 							button is disabled, otherwise enabled. 
	 * 							If null, editing is always enabled.  
	 * @return					A new {@link ListDeleteAction}
	 * @param <T>				The type of element in the list 
	 */
	public static <T> ListDeleteAction<T> create(
			Ref<ListBox<T>> list,
			Ref<Integer> selectionIndex,
			Target<T> target,
			Ref<Boolean> locked) {
		return new ListDeleteAction<T>(
				list,
				selectionIndex,
				target,
				locked,
				Messages.getString("ListDeleteAction.deleteText"), 
				icon, 
				Messages.getString("ListDeleteAction.deleteDescription"), 
				Messages.getInt("ListDeleteAction.deleteMnemonic")); //$NON-NLS-1$ //$NON-NLS-2$
	}
	
	/**
	 * Create an action with default icon. Text, mnemonic and tooltip set by resources.
	 * @param list				{@link Ref} to the {@link ListBox} to act on
	 * @param selectionIndex	{@link Ref} to the selection index of
	 * 							the item to delete.
	 * @param target			The target for deleted elements
	 * @param locked			{@link Ref} that controls editing - when true, 
	 * 							button is disabled, otherwise enabled. 
	 * 							If null, editing is always enabled.  
	 * @return					A new {@link ListDeleteAction}
	 * @param <T>				The type of element in the list 
	 */
	public static <T> ListDeleteAction<T> create(
			Ref<ListBox<T>> list,
			Ref<Integer> selectionIndex,
			Target<T> target) {
		return create(list, selectionIndex, target, null);
	}

	@Override
	public void actionPerformed(ActionEvent e) {
		
		//Perform actions atomically, in list's universe
		list.features().universe().internal().acquire();
		try {
			
			//Do nothing if locked
			if (Boxes.isTrue(locked)) {
				return;
			}

			int index = selectionIndex.get();
			ListBox<T> currentList = list.get();
	
			if (currentList == null) {
				return;
			}
			
			//Check current index is valid
			if (index < 0 || index >= currentList.size()) return;
			
			//Remove the element
			T removed = currentList.remove(index);
			
			if (target != null) {
				target.put(removed);
			}
			
		} finally {
			list.features().universe().internal().release();			
		}
	}

	@Override
	public void update() {
		
		int index = selectionIndex.get();
		ListBox<?> listBox = list.get();
		
		if (listBox == null) {
			setEnabled(false);
			return;
		}
		
		//Enabled when index is in list, and not locked
		setEnabled((!Boxes.isTrue(locked)) && (index >= 0) && (index < listBox.size()));
	}

}
